/*
 * Package il.ac.biu.cs.grossmm.test.test1
 * File Subscriber.java
 * Created on Aug 15, 2006
 *
 */
package il.ac.biu.cs.grossmm.tests.test1;

import static il.ac.biu.cs.grossmm.api.data.NodeTypeByInterface.nodeType;
import static il.ac.biu.cs.grossmm.api.keys.PatternFactory.mandatory;
import static il.ac.biu.cs.grossmm.api.keys.PatternFactory.pattern;
import il.ac.biu.cs.grossmm.api.OperationFailedException;
import il.ac.biu.cs.grossmm.api.data.Node;
import il.ac.biu.cs.grossmm.api.data.Root;
import il.ac.biu.cs.grossmm.api.data.UpdateType;
import il.ac.biu.cs.grossmm.api.flow.ActiveDataManager;
import il.ac.biu.cs.grossmm.api.flow.NodeEventInspector;
import il.ac.biu.cs.grossmm.api.flow.NodeInspector;
import il.ac.biu.cs.grossmm.api.flow.SubscriptionPoint;
import il.ac.biu.cs.grossmm.api.flow.Status;
import il.ac.biu.cs.grossmm.api.keys.ArrayKey;
import il.ac.biu.cs.grossmm.api.keys.Key;
import il.ac.biu.cs.grossmm.api.keys.KeyPattern;
import il.ac.biu.cs.grossmm.api.presence.BaseVocabulary;
import il.ac.biu.cs.grossmm.api.presence.ResourceType;
import il.ac.biu.cs.grossmm.api.server.Component;
import il.ac.biu.cs.grossmm.api.server.ComponentManager;
import il.ac.biu.cs.grossmm.api.server.ServiceManager;
import il.ac.biu.cs.grossmm.api.server.Service;

import java.util.Set;

import org.apache.log4j.Logger;
public class Subscriber extends Thread implements Component, Service, il.ac.biu.cs.grossmm.api.flow.Subscriber<Pidf> {
    /**
     * Logger for this class
     */
    private static final Logger logger = Logger.getLogger(Subscriber.class);

    ActiveDataManager adm;
    SubscriptionPoint<Pidf> sp;
    
    
    public Subscriber() {
        super("Subsciber");
    }

    public void aborted(Object hint, Exception e) {
        // TODO Implement aborted
        // 
        throw new RuntimeException("Not implemented");
    }

    public void setComponentManager(ComponentManager container) throws Exception {
        adm = (ActiveDataManager) container.getComponent(ActiveDataManager.class);
        
        KeyPattern pattern =
            pattern(String.class, mandatory(BaseVocabulary.RESOURCE_TYPE, ResourceType.PRESENCE));
        
        sp = adm.createSubscriptionPoint(this, pattern, nodeType(Pidf.class));
    }

    public void start(ServiceManager serviceManager) throws Exception {
        if (logger.isDebugEnabled()) {
            logger.debug("start() - start");
        }

        start();

        if (logger.isDebugEnabled()) {
            logger.debug("start(ServiceManager) - end");
        }
    }

    public void stop(Exception exception) {
        if (logger.isDebugEnabled()) {
            logger.debug("stop() - start");
        }

        interrupt();
        try {
            join();
        } catch (InterruptedException e) {
            logger.warn("stop(Exception) - exception ignored", e);
        }

        if (logger.isDebugEnabled()) {
            logger.debug("stop(Exception) - end");
        }
    }

    public void terminated(Object serviceHint, Exception e) {
        if( e != null )
            logger.error("TERMINATED ON EXCEPTION: "+e);
        stop(null);
    }

    public void activated(Key key, Root<Pidf> pidf) {
        logger.info("Activated "+key);
        
        NodeInspector ins = sp.readLock(pidf);
        
        try {
	        String entity = ins.getValue(pidf, Pidf.ENTITY);
	        logger.info("Entity = " + entity);
	        
	        Set<? extends Node<Tuple>> tuples = ins.getSubnodes(pidf, Pidf.TUPLES);
	        
	        for( Node<Tuple> tuple : tuples ) {
	            String id = ins.getValue(tuple, Tuple.ID);
	            boolean status = ins.getValue(tuple, Tuple.BASIC_STATUS);
	            String contact = ins.getValue(tuple, Tuple.CONTACT);
	            
	            logger.info("ID="+id+" Status="+status+" Contact="+contact);
	        }
        } catch( OperationFailedException e ) {
        	e.printStackTrace();
        }
    }

    public void deactivated(Key key, Status status) {
        
        logger.info("Deactivated ("+status+" for key "+key);

    }

    public void notify(Root<Pidf> pidf, NodeEventInspector ins) {
        if (logger.isDebugEnabled()) {
            logger.debug("notify() - start");
        }

        try {
	        String entity = ins.getValue(pidf, Pidf.ENTITY);
	        logger.info("Notification about " + entity);
	        
	        Set<Node<Tuple>> tuples = ins.getSubnodes(pidf, Pidf.TUPLES);
	        
	        for( Node<Tuple> tuple : tuples ) {
	            UpdateType u = ins.getUpdateType(tuple);
	            logger.info("Tuple. Update type: " +u); 
	            
	            String id = ins.getValue(tuple, Tuple.ID);
	            boolean status = ins.getValue(tuple, Tuple.BASIC_STATUS);
	            String contact = ins.getValue(tuple, Tuple.CONTACT);
	            
	            if( u == UpdateType.CHANGED ) {
	                String id0 = ins.getOldValue(tuple, Tuple.ID);
	                boolean status0 = ins.getOldValue(tuple, Tuple.BASIC_STATUS);
	                String contact0 = ins.getOldValue(tuple, Tuple.CONTACT);
	                logger.info("Old values ID="+id0+" Status="+status0+" Contact="+contact0);
	            }
	            
	            logger.info("ID="+id+" Status="+status+" Contact="+contact);
	        }
	        
	        tuples = ins.getSubnodes(pidf, Pidf.TUPLES, UpdateType.REMOVED);
	        for( Node<Tuple> tuple : tuples ) {
	            UpdateType u = ins.getUpdateType(tuple);
	            logger.info("Removed Tuple. Update type: " +u); 
	            
	            String id = ins.getValue(tuple, Tuple.ID);
	            boolean status = ins.getValue(tuple, Tuple.BASIC_STATUS);
	            String contact = ins.getValue(tuple, Tuple.CONTACT);
	
	            logger.info("ID="+id+" Status="+status+" Contact="+contact);
	        }
	
	        if (logger.isDebugEnabled()) {
	            logger.debug("notify(Node<Pidf>, NodeEventInspector) - end");
	        }
        } catch( OperationFailedException e) {
        	e.printStackTrace();
        }
    }
    
    @Override
    public void run() {
        
        try {
            sleep(1000);
        } catch (InterruptedException e) {
            return;
        }
        
        ArrayKey key = new ArrayKey("pres:Alice@example.com");
        key.addValue(BaseVocabulary.RESOURCE_TYPE, ResourceType.PRESENCE);
        
        logger.info("Subscribing to "+key);
        boolean b = sp.subscribe(key, null) == Status.UNMATCHED;
        if( b )
            logger.info("Subscribed to "+key);
        else
            logger.info("Subscription in progress to "+key);
    }
}
